/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { socket } from "@kit.NetworkKit";
import { CipherSuite } from "../CipherSuite";
import { Handshake } from "../Handshake";
import { Protocol } from "../Protocol";
import { TlsVersion } from "../TlsVersion";
import { SSLSocketFactory } from "./SSLSocketFactory";
import { TLSConnectOptions } from "./TLSConnectOptions";
import { X509TrustManager } from "./X509TrustManager";
import { Callback, ErrorCallback } from "@kit.BasicServicesKit";
import { SSLHandshakeException, SSLPeerUnverifiedException, IOException } from "../Error/error";

/**
 * Socket 事件类型枚举
 */
enum OnType {
  message = "message",
  connect = "connect",
  close = "close",
  error = "error"
}

/**
 * SSL 连接状态
 */
export enum SSLConnectionState {
  /** 未连接 */
  DISCONNECTED = 0,
  /** 连接中 */
  CONNECTING = 1,
  /** 已连接（TCP） */
  TCP_CONNECTED = 2,
  /** TLS 握手中 */
  HANDSHAKING = 3,
  /** 已连接（TLS） */
  TLS_CONNECTED = 4,
  /** 已关闭 */
  CLOSED = 5,
  /** 错误状态 */
  ERROR = 6
}

/**
 * SSL 连接事件监听器
 */
export interface SSLSocketEventListener {
  onConnecting?(): void;
  onConnected?(): void;
  onHandshakeStart?(): void;
  onHandshakeComplete?(handshake: Handshake, durationMs: number): void;
  onHandshakeFailed?(error: Error): void;
  onDisconnected?(): void;
  onError?(error: Error): void;
}

/**
 * SSL Session 封装类
 * 提供对 TLS 会话信息的访问
 */
export class SSLSession {
  private sslSocket: SSLSocket;

  constructor(sslSocket: SSLSocket) {
    this.sslSocket = sslSocket;
  }

  /**
   * 获取本地证书
   */
  getCertificate(): Promise<socket.X509CertRawData> {
    return this.sslSocket.getCertificate();
  }

  /**
   * 获取远程（服务器）证书
   */
  getRemoteCertificate(): Promise<socket.X509CertRawData> {
    return this.sslSocket.getRemoteCertificate();
  }

  /**
   * 获取协商的 TLS 协议版本
   */
  getProtocol(): Promise<string> {
    return this.sslSocket.getProtocol();
  }

  /**
   * 获取协商的加密套件
   */
  getCipherSuite(): Promise<Array<string>> {
    return this.sslSocket.getCipherSuite();
  }

  /**
   * 获取签名算法
   */
  getSignatureAlgorithms(): Promise<Array<string>> {
    return this.sslSocket.getSignatureAlgorithms();
  }

  /**
   * 获取 Handshake 对象
   * 包含 TLS 握手的详细信息
   */
  async handshake(): Promise<Handshake> {
    // 获取加密套件
    let cipherSuiteString: string[];
    try {
      cipherSuiteString = await this.getCipherSuite();
    } catch (e) {
      throw new SSLHandshakeException("Failed to get cipher suite: " + (e.message || e));
    }

    if (!cipherSuiteString || cipherSuiteString.length === 0) {
      throw new SSLHandshakeException("cipherSuite == null");
    }

    // 解析加密套件
    const cipherSuites: CipherSuite[] = [];
    for (const cs of cipherSuiteString) {
      if (cs === 'TLS_NULL_WITH_NULL_NULL' || cs === 'SSL_NULL_WITH_NULL_NULL') {
        throw new SSLHandshakeException(`Invalid cipher suite: ${cs}`);
      }
      try {
        cipherSuites.push(CipherSuite.forJavaName(cs));
      } catch (e) {
        console.warn(`[SSLSession] Unknown cipher suite: ${cs}`);
      }
    }

    if (cipherSuites.length === 0) {
      throw new SSLHandshakeException("No valid cipher suite found");
    }

    // 获取 TLS 版本
    let tlsVersionString: string;
    try {
      tlsVersionString = await this.getProtocol();
    } catch (e) {
      throw new SSLHandshakeException("Failed to get TLS version: " + (e.message || e));
    }

    if (!tlsVersionString || tlsVersionString === "NONE") {
      throw new SSLHandshakeException("tlsVersion == NONE");
    }

    const tlsVersion: TlsVersion = TlsVersion.forJavaName(tlsVersionString);

    // 获取证书
    let peerCertificatesCopy: socket.X509CertRawData | null = null;
    let localCertificates: socket.X509CertRawData | null = null;

    try {
      peerCertificatesCopy = await this.getRemoteCertificate();
    } catch (e) {
      console.warn("[SSLSession] Failed to get remote certificate:", e);
      peerCertificatesCopy = null;
    }

    // 尝试获取本地证书（可能会失败）
    try {
      localCertificates = await this.getCertificate();
    } catch (e) {
      // 错误码 2303502/2303505 表示没有客户端证书，这是正常的
      const errorCode = (e as { code?: number })?.code;
      if (errorCode !== 2303502 && errorCode !== 2303505) {
        console.warn("[SSLSession] Failed to get local certificate:", e);
      }
      localCertificates = null;
    }

    return new Handshake(
      tlsVersion,
      cipherSuites[0].javaName,
      localCertificates,
      () => peerCertificatesCopy
    );
  }
}

/**
 * SSLSocket 封装类
 *
 * 对鸿蒙 TLSSocket 的封装，提供：
 * - TLS/SSL 连接管理
 * - SNI (Server Name Indication) 支持
 * - ALPN (Application-Layer Protocol Negotiation) 支持
 * - 会话信息获取
 * - 错误处理和事件监听
 */
export class SSLSocket implements socket.TLSSocket {
  private socketObject: socket.TLSSocket;
  private protocols: string[] = [];
  private cipherSuites: string[] = [];
  private alpnProtocols: Protocol[] = [];
  private _sslSocketFactory: SSLSocketFactory = new SSLSocketFactory();
  private _connectionState: SSLConnectionState = SSLConnectionState.DISCONNECTED;
  private _eventListener: SSLSocketEventListener | null = null;
  private _sniHostname: string | null = null;

  /**
   * 获取 SocketFactory
   */
  get socketFactory(): SSLSocketFactory {
    return this._sslSocketFactory;
  }

  /**
   * 设置 SocketFactory
   */
  set sslSocketFactory(factory: SSLSocketFactory) {
    this._sslSocketFactory = factory;
  }

  /**
   * 获取 SSL Session
   */
  get session(): SSLSession {
    return new SSLSession(this);
  }

  /**
   * 获取连接状态
   */
  get connectionState(): SSLConnectionState {
    return this._connectionState;
  }

  /**
   * 设置事件监听器
   */
  set eventListener(listener: SSLSocketEventListener | null) {
    this._eventListener = listener;
  }

  /**
   * 获取 SNI 主机名
   */
  get sniHostname(): string | null {
    return this._sniHostname;
  }

  /**
   * 构造函数
   * @param tlsSocket 底层 TLSSocket（可选）
   */
  constructor(tlsSocket?: socket.TLSSocket) {
    this.socketObject = tlsSocket!;
    this.protocols = [socket.Protocol.TLSv12, socket.Protocol.TLSv13];
    this.cipherSuites = this.getDefaultCipherSuites();
  }

  /**
   * 获取默认的加密套件列表
   */
  private getDefaultCipherSuites(): string[] {
    return [
      CipherSuite.TLS_AES_128_GCM_SHA256.javaName,
      CipherSuite.TLS_AES_256_GCM_SHA384.javaName,
      CipherSuite.TLS_CHACHA20_POLY1305_SHA256.javaName,
      CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.javaName,
      CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384.javaName,
      CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256.javaName,
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256.javaName,
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384.javaName,
      CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256.javaName,
      CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA.javaName,
      CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA.javaName,
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA.javaName,
      CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA.javaName,
      CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256.javaName,
      CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384.javaName,
      CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.javaName,
      CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA.javaName,
      CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV.javaName,
    ];
  }

  /**
   * 初始化 SSL 配置
   */
  init(cert: string, key: string, tm: X509TrustManager): void {
    this._sslSocketFactory.cert = cert;
    this._sslSocketFactory.key = key;
    this._sslSocketFactory.tm = tm;
  }

  /**
   * 获取单例实例
   */
  static getInstance(): SSLSocket {
    return new SSLSocket();
  }

  /**
   * 获取支持的加密套件
   */
  get supportedCipherSuites(): string[] {
    return this.cipherSuites;
  }

  /**
   * 设置启用的协议版本
   */
  set enabledProtocols(protocols: string[]) {
    this.protocols = protocols;
  }

  /**
   * 获取启用的协议版本
   */
  get enabledProtocols(): string[] {
    return this.protocols;
  }

  /**
   * 设置启用的加密套件
   */
  set enabledCipherSuites(cipherSuites: string[]) {
    this.cipherSuites = cipherSuites;
  }

  /**
   * 获取启用的加密套件
   */
  get enabledCipherSuites(): string[] {
    return this.cipherSuites;
  }

  /**
   * 配置 TLS 扩展（SNI 和 ALPN）
   * @param host 服务器主机名（用于 SNI）
   * @param protocols ALPN 协议列表
   */
  configureTlsExtensions(host: string, protocols: Array<Protocol>): void {
    // 设置 SNI 主机名
    this._sniHostname = host;

    // 设置 ALPN 协议
    this.alpnProtocols = [...protocols];
  }

  /**
   * 获取配置的 ALPN 协议列表
   */
  getAlpnProtocols(): string[] {
    return this.alpnProtocols.map(p => p.toString());
  }

  /**
   * 启动 TLS 握手
   * @param tlsOptions TLS 连接选项
   */
  async startHandshake(tlsOptions: TLSConnectOptions): Promise<void> {
    this._connectionState = SSLConnectionState.HANDSHAKING;
    this._eventListener?.onHandshakeStart?.();

    const startTime = Date.now();

    try {
      await this.connect(tlsOptions);

      const duration = Date.now() - startTime;
      this._connectionState = SSLConnectionState.TLS_CONNECTED;

      // 获取握手信息
      try {
        const handshake = await this.session.handshake();
        this._eventListener?.onHandshakeComplete?.(handshake, duration);
        console.info(`[SSLSocket] TLS handshake completed in ${duration}ms`);
      } catch (e) {
        // 握手成功但获取信息失败，记录但不抛出
        console.warn("[SSLSocket] Failed to get handshake info:", e);
        this._eventListener?.onHandshakeComplete?.(null as unknown as Handshake, duration);
      }
    } catch (e) {
      this._connectionState = SSLConnectionState.ERROR;
      const error = this.wrapError(e, "TLS handshake failed");
      this._eventListener?.onHandshakeFailed?.(error);
      throw error;
    }
  }

  /**
   * 包装错误为适当的异常类型
   */
  private wrapError(e: unknown, context: string): Error {
    if (e instanceof Error) {
      if (e instanceof SSLHandshakeException || e instanceof SSLPeerUnverifiedException) {
        return e;
      }

      // 解析错误码
      const errorCode = (e as { code?: number })?.code;
      const message = `${context}: ${e.message}`;

      // 根据错误码返回适当的异常类型
      switch (errorCode) {
        case 2303502: // 证书验证失败
        case 2303505: // 证书错误
          return new SSLHandshakeException(`Certificate verification failed: ${e.message}`);
        case 2303104: // 连接被拒绝
        case 2303109: // 连接超时
          return new IOException(message);
        default:
          return new SSLHandshakeException(message);
      }
    }

    return new SSLHandshakeException(`${context}: ${String(e)}`);
  }

  /**
   * 绑定本地地址
   */
  bind(address: socket.NetAddress): Promise<void> {
    return this.socketObject.bind(address);
  }

  /**
   * 获取远程地址
   */
  getRemoteAddress(): Promise<socket.NetAddress> {
    return this.socketObject.getRemoteAddress();
  }

  /**
   * 获取 Socket 状态
   */
  getState(): Promise<socket.SocketStateBase> {
    return this.socketObject.getState();
  }

  /**
   * 设置额外选项
   */
  setExtraOptions(options: socket.TCPExtraOptions): Promise<void> {
    return this.socketObject.setExtraOptions(options);
  }

  /**
   * 获取本地地址
   */
  getLocalAddress(): Promise<socket.NetAddress> {
    return this.socketObject.getLocalAddress();
  }

  /**
   * 注册事件监听
   */
  on(type: string, callback: Callback<socket.SocketMessageInfo> | Callback<void> | ErrorCallback): void {
    switch (type) {
      case OnType.message:
        this.socketObject.on(type, callback as Callback<socket.SocketMessageInfo>);
        break;
      case OnType.connect:
        this.socketObject.on(type, callback as Callback<void>);
        break;
      case OnType.close:
        this.socketObject.on(type, callback as Callback<void>);
        break;
      case OnType.error:
        this.socketObject.on(type, callback as ErrorCallback);
        break;
      default:
        console.warn(`[SSLSocket] Unknown event type: ${type}`);
    }
  }

  /**
   * 取消事件监听
   */
  off(type: string, callback?: Callback<socket.SocketMessageInfo> | Callback<void> | ErrorCallback): void {
    switch (type) {
      case OnType.message:
        this.socketObject.off(type, callback as Callback<socket.SocketMessageInfo>);
        break;
      case OnType.connect:
        this.socketObject.off(type, callback as Callback<void>);
        break;
      case OnType.close:
        this.socketObject.off(type, callback as Callback<void>);
        break;
      case OnType.error:
        this.socketObject.off(type, callback as ErrorCallback);
        break;
      default:
        console.warn(`[SSLSocket] Unknown event type: ${type}`);
    }
  }

  /**
   * 获取本地证书
   */
  getCertificate(): Promise<socket.X509CertRawData> {
    return this.socketObject.getCertificate();
  }

  /**
   * 获取远程证书
   */
  getRemoteCertificate(): Promise<socket.X509CertRawData> {
    return this.socketObject.getRemoteCertificate();
  }

  /**
   * 获取协商的协议版本
   */
  getProtocol(): Promise<string> {
    return this.socketObject.getProtocol();
  }

  /**
   * 获取协商的加密套件
   */
  getCipherSuite(): Promise<Array<string>> {
    return this.socketObject.getCipherSuite();
  }

  /**
   * 获取签名算法
   */
  getSignatureAlgorithms(): Promise<Array<string>> {
    return this.socketObject.getSignatureAlgorithms();
  }

  /**
   * 获取 Socket 文件描述符
   */
  getSocketFd(): Promise<number> {
    return this.socketObject.getSocketFd();
  }

  /**
   * 连接到服务器
   */
  connect(options: socket.TLSConnectOptions): Promise<void> {
    this._connectionState = SSLConnectionState.CONNECTING;
    this._eventListener?.onConnecting?.();
    return this.socketObject.connect(options);
  }

  /**
   * 发送数据
   */
  send(data: string | ArrayBuffer): Promise<void> {
    return this.socketObject.send(data);
  }

  /**
   * 关闭连接
   */
  async close(): Promise<void> {
    try {
      await this.socketObject.close();
    } finally {
      this._connectionState = SSLConnectionState.CLOSED;
      this._eventListener?.onDisconnected?.();
    }
  }

  /**
   * 检查是否已连接
   */
  isConnected(): boolean {
    return this._connectionState === SSLConnectionState.TLS_CONNECTED;
  }

  /**
   * 检查是否已关闭
   */
  isClosed(): boolean {
    return this._connectionState === SSLConnectionState.CLOSED ||
      this._connectionState === SSLConnectionState.DISCONNECTED;
  }
}
